//-----------------------------------------------------------------------------
//
// reqHandler.c
//
// Group:
//    Doug Kline,          dakline@ncsu.edu
//    Mauricio Del Carpio, mgdelcar@ncsu.edu
//    Josh Jodrey,         jdjodrey@ncsu.edu
//
//
// This class handles a single TCP connection request containing an HTTP GET
// command.  Once the command is received, it is passed to the cache for
// fulfilment.
//
//-----------------------------------------------------------------------------

#include <linux/fs.h>
#include <linux/delay.h>

#include "reqHandler.h"
#include "cache.h"


// Debug facility, see proxy.c
extern int debug;

extern struct Config g_config;

static int sendStrToClient(struct ClientInfo *, char *str, int length, int pause);
static int parseCommand(char *command_p, char **method_pp, char **host_pp, char **url_pp, char **http_pp, char **version_pp);
static int checkCommand(struct ClientInfo *, char *method_p, char *host_p, char *url_p, char *http_p, char *version_p, char **context_type_pp);


//-----------------------------------------------------------------------------
// reqHandlerCreate()
//
// This is the function called by proxy when it spawns a new thread.  This
// function instantiates the reqHandler object to execute in.
//
//-----------------------------------------------------------------------------
int reqHandlerCreate(void *void_p)
{
   struct ClientInfo *clientInfo_p;
   struct task_struct *thread_p;
   
   if (void_p == 0)
   {
      printk(KERN_ERR "ERROR: TCPhandler::create() entered with a NULL pointer\n");
      return -1;
   }

   clientInfo_p = (struct ClientInfo *)void_p;

   if(debug) printk(KERN_DEBUG "\nStarted thread ID = %p\n", clientInfo_p->thread_p);

   reqHandlerRun(clientInfo_p);


   // Done with the socket
   if (clientInfo_p->clientFD != NULL)
   {
      if(debug) printk(KERN_DEBUG "kcache closing client connection\n");
      kclose(clientInfo_p->clientFD);
      clientInfo_p->clientFD = NULL;
   }

   // Remember the threadID for print
   thread_p = clientInfo_p->thread_p;

   // Clean up the memory
   
   kfree(clientInfo_p);
   clientInfo_p = NULL;

   if(debug) printk(KERN_DEBUG "Exiting thread ID = %u\n\n", (unsigned int)thread_p);
   return(0);

} // end reqHandlerCreate()

/*void
splitString(const char* str, char** results, size_t max, size_t* words)
{
    int start, pos = 0, strSize;
    *words = 0;

    while( str[pos] != '\0')
    {
        // Consume spaces while there is no end of string
        while (str[pos] == ' ' && str[pos] != '\0') pos++;

        start = pos;
        // Move through the word if it is not a space or end of string
        while (str[pos] != ' ' && str[pos] != '\0') pos++;

        // It is a new word if it has a length greater than 0
        if (start != pos)
        {
            strSize = pos - start;
            results[*words] = (char*) kmalloc(strSize + 1, GFP_KERNEL);
            strncpy(results[*words], str + start, strSize);
            results[*words][strSize] = '\0';
            (*words)++;
        }
    }
}


int
parseGetRequest(const char* request, char* requestValues[])
{
    size_t wordCount;
    splitString(request, requestValues, MAX_NUM_WORDS, &wordCount);

    if (wordCount != 3)
    {
        printk(KERN_ERR "parseGetRequest() The GET command does not have the regular form.");
        return -1;
    }

    if (strcmp(requestValues[0], "GET") != 0)
    {
        printk(KERN_ERR "parseGetRequest() Incorrect request method: %s.", requestValues[0]);
        return -1;
    }

    if (strcmp(requestValues[2], "HTTP/1.0") != 0)
    {
        printk(KERN_ERR "parseGetRequest() Protocol version unhandled: %s .", requestValues[2]);
        return -1;
    }

    if (strlen(requestValues[1]) == 1 && requestValues[1][0] != '/')
    {
        printk(KERN_ERR "parseGetRequest() Incorrect request URI: %s.", requestValues[1]);
        return -1;
    }

    return 0;
}*/


//-----------------------------------------------------------------------------
// reqHandlerRun()
//
// This function reads in the client's request and sends back the requested file.
//
//-----------------------------------------------------------------------------
int reqHandlerRun(struct ClientInfo *clientInfo_p)
{
   int err;
   int num;
   struct cacheItem *item_p;
   char *method_p;
   char *host_p;
   char *path_p;
   char *http_p;
   char *version_p;
   char *context_type_p;

   int count = 0;
   int done = 0;
   int length;
   
   char *command_p      = NULL;
   char *wholeCommand_p = NULL;
   int retVal = 1;
   
   printk(KERN_DEBUG "START: reqHandlerRun()\n");
   
   command_p      = kmalloc(COMMAND_BUFSIZE, GFP_KERNEL);
   wholeCommand_p = kmalloc(COMMAND_BUFSIZE, GFP_KERNEL);
   
   if ((command_p == NULL) || (wholeCommand_p == NULL))
   {
      printk(KERN_ERR "ERROR: Failed to kmalloc buffers\n");
      goto error2;
   }
   
   memset (command_p,      0, COMMAND_BUFSIZE);
   memset (wholeCommand_p, 0, COMMAND_BUFSIZE);
 
   if (clientInfo_p == NULL)
   {
      printk("ERROR: clientInfo_p == NULL\n");
      goto error2;
   }
   
   //--------------------------------------------------------------------------
   // Read the command from the client
   num = krecv(clientInfo_p->clientFD, command_p, COMMAND_BUFSIZE-1, 0);

   if (num < 0)
   {
      printk(KERN_ERR "ERROR: error reading from socket\n");
      goto error2;

   }
   else
   {
      // Debug: print out what was received from the client
      command_p[COMMAND_BUFSIZE-1] = '\0'; // Make sure string is NULL terminated
      if(debug) printk(KERN_DEBUG "\nCOMMAND RECEIVED:\n%s\n\n", command_p); 
   }
   
   // Make a copy
   strcpy(wholeCommand_p, command_p);

   //--------------------------------------------------------------------------
   // Parse and check the GET command
#if 1  //TODO
   err = parseCommand(command_p, &method_p, &host_p, &path_p, &http_p, &version_p);

   if (err)
   {
      printk(KERN_ERR "ERROR: parse command failed\n");  //TODO: May not want this statement
      // Nothing more to do
      goto error2;
   }
#endif

   // At this point, command_p has been broken up and the other pointers are
   // pointing into it.  Don't touch command_p any further.  wholeCommand_p
   // retains the whole command.

   /*err = checkCommand(clientInfo_p, method_p, host_p, path_p, http_p, version_p, &context_type_p);

   if (err)
   {
      // Nothing more to do
      goto error2;
   }*/
      
   // Get the cache item.  If not cached, then it will be retreived from the
   // server.  item_p is locked on return.
   item_p = cacheGet(wholeCommand_p, host_p, path_p, clientInfo_p->clientAddr);

   if (item_p == NULL)
   {
      // Nothing more to do
      goto error2;
   }

   // Send the buffer
   while( !done)
   {  
      if ( (item_p->size - count) > RESPONSE_BUFSIZE )
      {
         length = RESPONSE_BUFSIZE;
      }
      else
      {
         length = item_p->size - count;
         done = 1;
      }
    
      num = ksend(clientInfo_p->clientFD, (item_p->buf_p)+count, length, 0);

      if (num != length)
      {
         printk(KERN_ERR "ERROR: failed sending response to client, returned count = %d\n", count);
         break;
      }

      count += length;
   
   }

   retVal = 0;

error1:   
   // Done with the cache item
   unlock(item_p);

error2:
   if (command_p)      kfree(command_p);
   if (wholeCommand_p) kfree(wholeCommand_p);  

   printk(KERN_DEBUG "END: reqHandlerRun()\n");
   
   return retVal; 
   
} // end reqHandlerRun()


//-----------------------------------------------------------------------------
// sendStrToClient()
//
// This function send a string back to the client.  This could be the response
// header or an error string.
//
//-----------------------------------------------------------------------------
int sendStrToClient(struct ClientInfo *clientInfo_p, char *str, int strLength, int pause)
{
   // Send the response header
   int count = ksend(clientInfo_p->clientFD, str, strLength, 0);

   if (count != strLength)
   {
      printk(KERN_ERR "ERROR: failed sending response to client, returned count = %d\n", count);
      return 1;
   }

   if (pause)
   {
      // Don't close connection immediately
      msleep_interruptible(1);
   }

   return 0;

} // end sendStrToClient()



//-----------------------------------------------------------------------------
// parseCommand()
//
// This function parses the command from the client to find the requested file.
//
//-----------------------------------------------------------------------------
int parseCommand(char *command_p, char **method_pp, char **host_pp, char **path_pp, char **http_pp, char **version_pp)
{

   // Expect a command like: GET /Protocols/rfc1945/rfc1945 HTTP/1.0
   //                        GET / HTTP/1.0
   
/*   
   From RFC2616
   
   5.2 The Resource Identified by a Request


   The exact resource identified by an Internet request is determined by
   examining both the Request-URI and the Host header field.

   An origin server that does not allow resources to differ by the
   requested host MAY ignore the Host header field value when
   determining the resource identified by an HTTP/1.1 request. (But see
   section 19.6.1.1 for other requirements on Host support in HTTP/1.1.)

   An origin server that does differentiate resources based on the host
   requested (sometimes referred to as virtual hosts or vanity host
   names) MUST use the following rules for determining the requested
   resource on an HTTP/1.1 request:

   1. If Request-URI is an absoluteURI, the host is part of the
     Request-URI. Any Host header field value in the request MUST be
     ignored.

   2. If the Request-URI is not an absoluteURI, and the request includes
     a Host header field, the host is determined by the Host header
     field value.

   3. If the host as determined by rule 1 or 2 is not a valid host on
     the server, the response MUST be a 400 (Bad Request) error message.

   Recipients of an HTTP/1.0 request that lacks a Host header field MAY
   attempt to use heuristics (e.g., examination of the URI path for
   something unique to a particular host) in order to determine what
   exact resource is being requested.
*/

   char *saveptr_p;
   char *path_p;

   printk(KERN_DEBUG "START: parseCommand()\n");

   saveptr_p = command_p;
  
   *method_pp = strsep(&saveptr_p, " "); //TODO: Problably don't need

   if (*method_pp == 0)
   {
      printk(KERN_DEBUG "#########1000\n"); //TODO
      return 1;
   }

   *path_pp = strsep(&saveptr_p, " ");
   path_p = *path_pp;

   if (path_p == 0)
   {
      printk(KERN_DEBUG "#########2000\n");  //TODO
      return 1;
   }

   if (strPrefixMatch("HTTP", path_p))
   {
      *http_pp = *path_pp;
      *path_pp = NULL;
   }
   else
   {
      // Get rid of the leading /
       if (*path_p == '/')
       {
          path_p++;
       }
       
       // If path starts with http://, then get rid of that too
       if ( strPrefixMatch("http://", path_p))
       {
          path_p = path_p+7;
       }
          
       *host_pp = path_p;
       
       // Now find the end of the host
       while ( (*path_p != '/') && (*path_p != ' ') )
       {
          path_p++;
       }
       
       *path_pp = path_p;

       if (*path_p == '/')
       {
          (*path_pp)++;     // step it past the '/'
          (*path_p) = '\0'; // and NULL terminate the host name
       }
          
       *http_pp = strsep(&saveptr_p, " /");

       if (*http_pp == 0)
       {
          printk(KERN_DEBUG "#########3000\n"); //TODO
          return 1;
       }
   }

   /**version_pp = strsep(&saveptr_p, " /\n\r");
   if (*version_pp == 0)
   {
      printk(KERN_DEBUG "#########4000\n"); //TODO
      return 1;
   }*/
   
   
   // Now find the Host: line
   while( *saveptr_p == '\0' )
   {
      saveptr_p++;
   }
      
   while(*saveptr_p != '\0')
   {
      if ( strPrefixMatch("Host: ", saveptr_p) )
      {
          saveptr_p += 6;
          *host_pp = strsep(&saveptr_p, " \n\r");
          break;
      }
      saveptr_p++;
   }   
   
   printk(KERN_DEBUG "END: parseCommand()\n"); //TODO
   return 0;

} // end parseCommand()


//-----------------------------------------------------------------------------
// checkCommand()
//
// This function checks the parsed tokens to make sure it is a well formed
// GET command.
//
//-----------------------------------------------------------------------------
int checkCommand(struct ClientInfo *clientInfo_p, char *method_p, char *host_p, char *path_p, char *http_p, char *version_p, char **context_type_pp)
{
   int  len;
   char *buf_p;
   int retVal = 1;

   //if(debug) printk(KERN_INFO "method=%s, host=%s, path=%s, http=%s, version=%s\n", method_p, host_p, path_p, http_p, version_p);

   buf_p = kmalloc(RESPONSE_BUFSIZE, GFP_KERNEL);

#if 1
   //--------------------------------------------------------------------------
   // dummy code
   
      if(debug) printk(KERN_DEBUG "Sending back canned response\n");
 
      len = sprintf(buf_p, "HTTP/1.0 400 Bad Request: Dummy Method: \r\n"
"Content-type: text/html\r\n\r\n"
"<BODY><H1>400 Dummy Request: Invalid Method: </H1></BODY>\r\n");

      sendStrToClient(clientInfo_p, buf_p, len, 1);

      goto error;
#endif

   //--------------------------------------------------------------------------
   // Check for the GET command
   // strcmp() returns 0 when the strings do compare
   if ( strcmp(method_p, "GET"))
   {
      if(debug) printk(KERN_DEBUG "Bad client command\n");
 
      len = sprintf(buf_p, "HTTP/1.0 400 Bad Request: Invalid Method: %s\r\n"
"Content-type: text/html\r\n\r\n"
"<BODY><H1>400 Bad Request: Invalid Method: %s</H1></BODY>\r\n", method_p, method_p);

      sendStrToClient(clientInfo_p, buf_p, len, 1);

      goto error;
   }

   //--------------------------------------------------------------------------
   // Check for HTTP - if we don't match then the URI was probably poorly formed
   // strcmp() returns 0 when the strings do compare
   if ( strcmp(http_p, "HTTP"))
   {
      if(debug) printk(KERN_DEBUG "Bad URI\n");
 
       len = sprintf(buf_p, "HTTP/1.0 400 Bad Request: Invalid URI: %s\r\n"
"Content-type: text/html\r\n\r\n"
"<BODY><H1>400 Bad Request: Invalid URI: %s</H1></BODY>\r\n", path_p, path_p);
 
      sendStrToClient(clientInfo_p, buf_p, len, 1);

      goto error;
   }


   //--------------------------------------------------------------------------
   // Check for version 1.0
   // strcmp() returns 0 when the strings do compare
   if ( strcmp(version_p, "1.0"))
   {
      if(debug) printk(KERN_DEBUG "Bad HTTP level\n");

      len = sprintf(buf_p, "HTTP/1.0 400 Bad Request: Invalid HTTP-Version: %s\r\n"
"Content-type: text/html\r\n\r\n"
"<BODY><H1>400 Bad Request: Invalid HTTP-Version: %s</H1></BODY>\r\n", version_p, version_p);

      sendStrToClient(clientInfo_p, buf_p, len, 1);

      goto error;
   }
   
   retVal = 0;

error:
   kfree(buf_p);
   return retVal;

} // end checkCommand()



//-----------------------------------------------------------------------------
// strPrefixMatch()
//
// This function compares two strings up to the length of the prefix to see
// if there is a match.
//
// Return: 1 if match
//         0 if no match
//
//-----------------------------------------------------------------------------
int strPrefixMatch(char *prefix, char *str)
{
   if ((prefix == NULL) || (str == NULL))
   {
      return 0;
   }
   
   while(*prefix != '\0')
   {
      if (*str == '\0')
         return 0;
         
      if (*prefix != *str)
         return 0;
         
      prefix++;
      str++;
   
   }
   
   return 1;


} // end strPrefixMatch()
